﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace TTApp.Phone.Silverlight.Hexagons
{
    /// <summary>
    /// 
    /// </summary>
    [ContentProperty("HexagonSlots")]
    public class Board : Panel
    {
        #region //----------------------------- Class Private Attributes ----------------------------------//
        private HexagonSlotCollection _hexagonSlots;
        private Dictionary<string, HexagonSlot> _hexagonSlotDictionary;
        private BoardSource _boardSource;
        #endregion

        #region //----------------------------- Attached Properties ---------------------------------------//
        /// <summary>
        /// Attached Property định danh cho property X
        /// </summary>
        public static readonly DependencyProperty XProperty;

        /// <summary>
        /// Attached Property định danh cho property Y
        /// </summary>
        public static readonly DependencyProperty YProperty;
        #endregion

        #region //----------------------------- Dependency Properties -------------------------------------//
        /// <summary>
        /// Dependency Property định danh cho Property HorizontalHexagonNumber
        /// </summary>
        public static readonly DependencyProperty HorizontalHexagonNumberProperty;

        /// <summary>
        /// Dependency Property định danh cho Property VerticalHexagonNumber
        /// </summary>
        public static readonly DependencyProperty VerticalHexagonNumberProperty;
        #endregion

        #region //----------------------------- Static Class Constructor ----------------------------------//
        static Board()
        {
            XProperty = DependencyProperty.RegisterAttached("X", typeof(double), typeof(Board), new PropertyMetadata(0.0, CoordinateProperty_Changed));
            YProperty = DependencyProperty.RegisterAttached("Y", typeof(double), typeof(Board), new PropertyMetadata(0.0, CoordinateProperty_Changed));

            HorizontalHexagonNumberProperty = DependencyProperty.Register("HorizontalHexagonNumber", typeof(int), typeof(Board), new PropertyMetadata(0, HexagonNumberProperty_Changed));
            VerticalHexagonNumberProperty = DependencyProperty.Register("VerticalHexagonNumber", typeof(int), typeof(Board), new PropertyMetadata(0, HexagonNumberProperty_Changed));
        }
        #endregion

        #region //----------------------------- Class Constructors ----------------------------------------//
        public Board()
        {
            // Để nhận sự kiện khi touch
            this.Background = new SolidColorBrush(Colors.Transparent);

            _hexagonSlots = new HexagonSlotCollection();
            _hexagonSlots.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(OnHexagonsCollectionChanged);

            _hexagonSlotDictionary = new Dictionary<string, HexagonSlot>();
        }
        #endregion

        #region //----------------------------- CLR Properties --------------------------------------------//
        /// <summary>
        /// Số lượng hexagon theo chiều ngang.
        /// </summary>
        public int HorizontalHexagonNumber
        {
            get { return (int)GetValue(HorizontalHexagonNumberProperty); }
            set { SetValue(HorizontalHexagonNumberProperty, value); }
        }

        /// <summary>
        /// Số lượng hexagon theo chiều dọc.
        /// </summary>
        public int VerticalHexagonNumber
        {
            get { return (int)GetValue(VerticalHexagonNumberProperty); }
            set { SetValue(VerticalHexagonNumberProperty, value); }
        }

        /// <summary>
        /// Collection chứa các HexagonSlot
        /// </summary>
        /// <remarks>Đây là phần remark :|</remarks>
        public HexagonSlotCollection HexagonSlots
        {
            get
            {
                return _hexagonSlots;
            }
        }

        /// <summary>
        /// Lấy hoặc gán nguồn của Board.
        /// </summary>
        public BoardSource Source
        {
            get
            {
                return _boardSource;
            }
            set
            {
                _boardSource = value;
                ApplyBoardSource(_boardSource);
            }
        }

        /// <summary>
        /// Set property này không có tác dụng gì =))
        /// </summary>
        public new UIElementCollection Children
        {
            private get { return base.Children; }
            set
            {

            }
        }
        #endregion

        #region //----------------------------- DP Changed Callbacks --------------------------------------//
        private static void CoordinateProperty_Changed(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            Board parent = VisualTreeHelper.GetParent(obj) as Board;

            if (parent != null)
            {
                parent.InvalidateArrange();
            }
        }

        private static void HexagonNumberProperty_Changed(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            (obj as Board).OnHexagonNumberChanged(args);
        }
        #endregion

        #region //----------------------------- Attached Property Accessors -------------------------------//
        /// <summary>
        /// Gán giá trị cho attached property X
        /// </summary>
        /// <param name="obj">Đối tượng được gán</param>
        /// <param name="value">Giá trị được gán</param>
        public static void SetX(DependencyObject obj, double value)
        {
            obj.SetValue(XProperty, value);
        }

        /// <summary>
        /// Lấy giá trị của X trên đối tượng obj
        /// </summary>
        /// <param name="obj">Đối tượng cần lấy giá trị</param>
        /// <returns>Giá trị của X trên đối tượng obj</returns>
        public static double GetX(DependencyObject obj)
        {
            return (double)obj.GetValue(XProperty);
        }

        /// <summary>
        /// Gán giá trị cho attached property Y
        /// </summary>
        /// <param name="obj">Đối tượng được gán</param>
        /// <param name="value">Giá trị được gán</param>
        public static void SetY(DependencyObject obj, double value)
        {
            obj.SetValue(YProperty, value);
        }

        /// <summary>
        /// Lấy giá trị của Y trên đối tượng obj
        /// </summary>
        /// <param name="obj">Đối tượng cần lấy giá trị</param>
        /// <returns>Giá trị của Y trên đối tượng obj</returns>
        public static double GetY(DependencyObject obj)
        {
            return (double)obj.GetValue(YProperty);
        }
        #endregion

        #region //----------------------------- Public Event Delegates ------------------------------------//
        /// <summary>
        /// Xảy ra khi giá trị của HorizontalHexagonNumber hoặc VerticalHexagonNumber thay đổi.
        /// </summary>
        public event DependencyPropertyChangedEventHandler HexagonNumberChanged;
        #endregion

        #region //----------------------------- Overridden Methods ----------------------------------------//
        /// <summary>
        /// Lấy kích thước nhỏ nhất có thể
        /// </summary>
        /// <param name="availableSize">kích thước dự định sẽ cho</param>
        /// <returns>kích thước mà đối tượng này cần</returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            if (HorizontalHexagonNumber <= 0 || VerticalHexagonNumber <= 0 || Children.Count == 0)
            {
                return Size.Empty;
            }

            Size hexagonSize = new Size(
                    availableSize.Width / (4 + (HorizontalHexagonNumber - 1) * 3) * 4,
                    availableSize.Height / VerticalHexagonNumber);
            Size hexagonMaxSize = new Size();

            foreach (UIElement child in Children)
            {
                if (child is HexagonSlot && _hexagonSlots.Contains(child as HexagonSlot))
                {
                    child.Measure(hexagonSize);
                    //child.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
                    hexagonMaxSize.Width = Math.Max(hexagonMaxSize.Width, child.DesiredSize.Width);
                    hexagonMaxSize.Height = Math.Max(hexagonMaxSize.Height, child.DesiredSize.Height);
                }
            }

            double actualBoardWidth = hexagonMaxSize.Width / 4 * (4 + (HorizontalHexagonNumber - 1) * 3);
            double actualBoardHeight = hexagonMaxSize.Height * VerticalHexagonNumber;

            if (Double.IsPositiveInfinity(availableSize.Width) && Double.IsPositiveInfinity(availableSize.Height))
            {
                return new Size(actualBoardWidth, actualBoardHeight);
            }
            else if (Double.IsPositiveInfinity(availableSize.Width) && !Double.IsPositiveInfinity(availableSize.Height))
            {
                return new Size(actualBoardWidth, Math.Min(availableSize.Height, actualBoardHeight));
            }
            else if (!Double.IsPositiveInfinity(availableSize.Width) && Double.IsPositiveInfinity(availableSize.Height))
            {
                return new Size(Math.Min(availableSize.Width, actualBoardWidth), actualBoardHeight);
            }
            else
            {
                return new Size(Math.Min(availableSize.Width, actualBoardWidth), Math.Min(availableSize.Height, actualBoardHeight));
            }
        }

        /// <summary>
        /// Xác định vị trí và kích thước của children.
        /// </summary>
        /// <param name="finalSize">Kích thước thực sự mà Board nhận được</param>
        /// <returns>Không rõ là trả về cái gì =,=</returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            if (HorizontalHexagonNumber > 0 && VerticalHexagonNumber > 0 && Children.Count > 0)
            {
                Rect availableArea = new Rect(0, 0, finalSize.Width, finalSize.Height);
                Size hexagonFinalSize = new Size(finalSize.Width / (4 + (HorizontalHexagonNumber - 1) * 3) * 4, finalSize.Height / VerticalHexagonNumber);

                foreach (UIElement child in Children)
                {
                    if (child is HexagonSlot && _hexagonSlots.Contains(child as HexagonSlot))
                    {
                        double x = Board.GetX(child);
                        double y = Board.GetY(child);

                        //Không cần điều kiện này nữa.
                        //if (((Math.Abs(x) % 2 == 0) && (Math.Abs(y) % 1 == 0)) || // x nguyên chẵn và y là số nguyên
                        //    ((Math.Abs(x) % 1 == 0) && (Math.Abs(x) % 2 != 0) && (Math.Abs(y) % 1 == 0.5))) // x nguyên lẻ và y là bội lẻ của 0.5
                        {
                            Rect childFinalPosition = new Rect(new Point(0.75 * hexagonFinalSize.Width * x, hexagonFinalSize.Height * y), hexagonFinalSize);
                            childFinalPosition.Intersect(availableArea);

                            if (!childFinalPosition.IsEmpty)
                            {
                                child.Arrange(childFinalPosition);
                            }
                        }
                    }
                }
            }

            return base.ArrangeOverride(finalSize);
        }
        #endregion

        #region //----------------------------- Protected Event Invokers ----------------------------------//
        /// <summary>
        /// Được gọi khi số lượng của hexagon thay đổi.
        /// </summary>
        /// <param name="args"></param>
        protected virtual void OnHexagonNumberChanged(DependencyPropertyChangedEventArgs args)
        {
            this.InvalidateMeasure();

            if (HexagonNumberChanged != null)
            {
                HexagonNumberChanged(this, args);
            }
        }
        #endregion

        #region //----------------------------- Private Event Handlers ------------------------------------//
        /// <summary>
        /// Được gọi khi có HexagonSlot được thêm vào collection.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void OnHexagonsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
        {
            if (args.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (HexagonSlot hexagonSlot in args.NewItems)
                {
                    this.Children.Add(hexagonSlot);

                    double x = Board.GetX(hexagonSlot);
                    double y = Board.GetY(hexagonSlot);

                    _hexagonSlotDictionary.Add(String.Format("{0} {1}", x, y), hexagonSlot);
                }
            }
            else if (args.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (HexagonSlot hexagonSlot in args.OldItems)
                {
                    this.Children.Remove(hexagonSlot);

                    double x = Board.GetX(hexagonSlot);
                    double y = Board.GetY(hexagonSlot);

                    _hexagonSlotDictionary.Remove(String.Format("{0} {1}", x, y));
                }
            }
            else if (args.Action == NotifyCollectionChangedAction.Reset)
            {
                base.Children.Clear();
                _hexagonSlotDictionary.Clear();
            }
        }
        #endregion

        #region //----------------------------- Class Public Methods --------------------------------------//
        /// <summary>
        /// Lấy HexagonSlot ở tọa độ (x, y). Nếu không có thì trả về null
        /// </summary>
        public HexagonSlot GetHexagonSlot(double x, double y)
        {
            HexagonSlot hexagonSlot;
            _hexagonSlotDictionary.TryGetValue(String.Format("{0} {1}", x, y), out hexagonSlot);

            return hexagonSlot;
        }

        /// <summary>
        /// Lấy Hexagon nằm trong HexagonSlot có vị trí (x, y).
        /// </summary>
        /// <param name="x">Tọa độ X của HexagonSlot.</param>
        /// <param name="y">Tọa độ Y của HexagonSlot.</param>
        /// <returns>Hexagon nằm trong HexagonSlot có tọa độ (x, y).</returns>
        public Hexagon GetHexagon(double x, double y)
        {
            Hexagon hexagon = null;

            HexagonSlot slot = this.GetHexagonSlot(x, y);
            if (slot != null && !slot.IsEmpty)
            {
                hexagon = slot.Hexagon;
            }

            return hexagon;
        }

        /// <summary>
        /// Lấy các ô HexagonSlot hàng xóm của Hexagon theo khoảng cách.
        /// </summary>
        /// <param name="hexagon">Hexagon cần lấy hàng xóm.</param>
        /// <param name="distance">Khoảng cách từ Hexagon đến hàng xóm.</param>
        /// <returns></returns>
        public List<HexagonSlot> GetNeighbourHexagonSlots(Hexagon hexagon, int distance)
        {
            HexagonSlot slot = VisualTreeHelper.GetParent(hexagon) as HexagonSlot;
            if (slot == null)
            {
                throw new InvalidOperationException("Hexagon chưa được gắn vào HexagonSlot");
            }

            if (!this.Contains(slot))
            {
                throw new ArgumentException("hexagon", "Hexagon không nằm trong Board này.");
            }

            return slot.GetNeighbours(distance);
        }

        /// <summary>
        /// Kiểm tra xem Board này có chứa HexagonSlot được truyền vào hay không.
        /// </summary>
        /// <param name="hexagonSlot">HexagonSlot được kiểm tra.</param>
        /// <returns></returns>
        public bool Contains(HexagonSlot hexagonSlot)
        {
            return _hexagonSlotDictionary.ContainsValue(hexagonSlot);
        }
        #endregion

        #region //----------------------------- Class Private Methods -------------------------------------//
        /// <summary>
        /// Áp dụng nguồn cho Board. Các thông tin về Board được thêm vào theo đối tượng source.
        /// </summary>
        /// <param name="source">Nguồn của Board.</param>
        private void ApplyBoardSource(BoardSource source)
        {
            if (source is SlottedBoard)
            {
                SlottedBoard slottedBoard = source as SlottedBoard;

                this.HorizontalHexagonNumber = slottedBoard.HorizontalHexagonSlotNumber;
                this.VerticalHexagonNumber = slottedBoard.VerticalHexagonSlotNumber;

                _hexagonSlots.Clear();
                foreach (HexagonSlot slot in slottedBoard.HexagonSlots)
                {
                    _hexagonSlots.Add(slot);
                }
            }
        }
        #endregion

        #region //----------------------------- Class Static Methods --------------------------------------//
        /// <summary>
        /// Tạo 1 đối tượng BoardSource từ file XML.
        /// </summary>
        /// <param name="pathToXml">Đường dẫn đến file XML.</param>
        /// <returns></returns>
        public static BoardSource GetBoardSourceFromFile(string pathToXml)
        {
            return new BoardSourceConverter().ConvertFrom(pathToXml) as BoardSource;
        }
        #endregion
    }
}
